{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Lesson 6\n",
    "\n",
    "Python Basic, Lesson 6, v1.0.1, 2017.1 by David.Yi   \n",
    "Python Basic, Lesson 6, v1.0.2, 2017.5 modified by Yimeng.Zhang  \n",
    "\n",
    "\n",
    "### 上次内容要点\n",
    "\n",
    "* 文件和目录操作之一：文件和目录操作\n",
    "* 文件和目录操作之二：读写文本文件\n",
    "* 搜索硬盘上指定路径指定类型的文件\n",
    "    \n",
    "### 本次内容要点\n",
    "\n",
    "* 集合库 collections 简介\n",
    "    * namedtuple: 生成可以使用名字来访问元素内容的tuple子类\n",
    "    * deque: 双端队列，可以快速的从另外一侧追加和推出对象\n",
    "    * defaultdict: 带有默认值的字典\n",
    "    * OrderedDict: 有序字典\n",
    "    * Counter: 计数器\n",
    "* 错误处理介绍\n",
    "* PEP 8 规定以及 python 编程基本规范\n",
    "* 单元测试基础\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "### collections\n",
    "\n",
    "collections 是 Python 内建的一个集合模块，提供了许多有用的集合类。我们来完整的看看 collections 提供了哪些扩展功能。\n",
    "\n",
    "python 内置了大量的功能函数，但是再多的函数和模块也不可能覆盖实际应用需要的所有功能，怎么扩展程序功能，怎么划分功能模块，python 自己所带的这些模块是最好的参考书！\n",
    "\n",
    "* namedtuple: 生成可以使用名字来访问元素内容的tuple子类\n",
    "* deque: 双端队列，可以快速的从另外一侧追加和推出对象\n",
    "* defaultdict: 带有默认值的字典\n",
    "* OrderedDict: 有序字典\n",
    "* Counter: 计数器"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "#### namedtuple 有名字的元组\n",
    "\n",
    "namedtuple 主要用来产生可以使用名称来访问元素的数据对象，通常用来增强代码的可读性，在访问一些 tuple 类型的数据时尤其好用。\n",
    "\n",
    "namedtuple 是一个函数，它用来创建一个自定义的 tuple 对象，并且规定了 tuple 元素的个数，可以用属性而不是索引来写入或者访问 tuple 的某个元素。\n",
    "\n",
    "这样一来，我们用 namedtuple 可以很方便地定义一种数据类型，比如 XY 坐标，它具备 tuple 的内容不变性，又可以根据属性来引用，使用十分方便。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1 2\n"
     ]
    }
   ],
   "source": [
    "# nametuple 举例\n",
    "\n",
    "from collections import namedtuple\n",
    "\n",
    "Point = namedtuple('Point', ['x', 'y'])\n",
    "p = Point(1, 2)\n",
    "print(p.x, p.y)  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "3\n"
     ]
    }
   ],
   "source": [
    "i = p.x + p.y\n",
    "print(i)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "web(name='google', type='search', url='www.google.com')\n"
     ]
    }
   ],
   "source": [
    "# nametuple 举例\n",
    "\n",
    "from collections import namedtuple\n",
    "\n",
    "Web = namedtuple('web', ['name', 'type', 'url'])\n",
    "p1 = Web('google', 'search', 'www.google.com')\n",
    "p2 = Web('sina', 'portal', 'www.sina.com.cn')\n",
    "\n",
    "print(p1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "google www.google.com\n"
     ]
    }
   ],
   "source": [
    "print(p1.name, p1.url)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "www.google.com www.sina.com.cn\n"
     ]
    }
   ],
   "source": [
    "print(p1.url, p2.url)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "sina\n",
      "portal\n",
      "www.sina.com.cn\n"
     ]
    }
   ],
   "source": [
    "# 遍历 nametuple\n",
    "\n",
    "for i in p2:\n",
    "    print(i)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[web(name='google', type='search', url='www.google.com'), web(name='sina', type='portal', url='www.sina.com.cn')]\n",
      "google\n",
      "sina\n"
     ]
    }
   ],
   "source": [
    "# 复杂的基于 namedtuple list demo\n",
    "from collections import namedtuple\n",
    "\n",
    "Web = namedtuple('web', ['name', 'type', 'url'])\n",
    "\n",
    "p = []\n",
    "p.append(Web('google', 'search', 'www.google.com'))\n",
    "p.append(Web('sina', 'portal', 'www.sina.com.cn'))\n",
    "print(p)\n",
    "\n",
    "for i in p:\n",
    "    print(i.name)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "('name', 'type', 'url')\n"
     ]
    }
   ],
   "source": [
    "# 显示 namedtuple 的字段名称\n",
    "\n",
    "print(Web._fields)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "#### deque\n",
    "\n",
    "使用 list 存储数据时，按索引访问元素很快，但是插入和删除元素就很慢了，因为 list 是线性存储，数据量大的时候，插入和删除效率很低。\n",
    "\n",
    "deque 是为了高效实现插入和删除操作的双向列表，适合用于队列和栈。\n",
    "\n",
    "deque 在插入数据时候速度比 list 快很多，当然这个是相对存在大量数据的 list 而言的。如果你的程序中需要对有百万级别的 list 频繁的在各个位置插入删除数据，那么用 deque 是值得的。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "deque(['y', 'a', 'b', 'c', 'x'])\n"
     ]
    }
   ],
   "source": [
    "# deque 举例\n",
    "\n",
    "from collections import deque\n",
    "\n",
    "q = deque(['a', 'b', 'c'])\n",
    "q.append('x')\n",
    "q.appendleft('y')\n",
    "\n",
    "print(q)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": false,
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.008998870849609375\n",
      "0.0\n"
     ]
    }
   ],
   "source": [
    "# 对比 list 和 deque 的速度\n",
    "\n",
    "from collections import deque\n",
    "import time\n",
    "\n",
    "# list\n",
    "q0 = [x*x for x in range(10000000)]\n",
    "\n",
    "# list\n",
    "a = time.time()\n",
    "q0.insert(0,888)\n",
    "# q0.append(999)\n",
    "b = time.time()\n",
    "print(b-a)\n",
    "\n",
    "# 生成 deque\n",
    "q1= deque(q0)\n",
    "\n",
    "# deque\n",
    "a = time.time()\n",
    "q1.appendleft(888)\n",
    "# q1.append(999)\n",
    "b = time.time()\n",
    "print(b-a)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "deque(['a', 'b', 'c', 'd'])\n"
     ]
    }
   ],
   "source": [
    "from collections import deque\n",
    "\n",
    "l = ['a','b','c','d']\n",
    "l = deque(l)\n",
    "print(l)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "deque(['d', 'e', 'a', 'b', 'c'])\n",
      "deque(['a', 'b', 'c', 'd', 'e'])\n"
     ]
    }
   ],
   "source": [
    "# deque rotation\n",
    "\n",
    "l = ['a','b','c','d','e']\n",
    "l = deque(l)\n",
    "l.rotate(2)\n",
    "print(l)\n",
    "l.rotate(-2)\n",
    "print(l)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "deque(['a', 'b'])\n"
     ]
    }
   ],
   "source": [
    "# deque pop() 同样可以区分头尾\n",
    "\n",
    "l = deque(['a','b','c'])\n",
    "l.pop()\n",
    "print(l)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "deque(['b', 'c'])\n"
     ]
    }
   ],
   "source": [
    "l = deque(['a','b','c'])\n",
    "l.popleft()\n",
    "print(l)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "#### defaultdict\n",
    "\n",
    "我们都知道，在使用Python原生的数据结构dict的时候，如果用 d[key] 这样的方式访问， 当指定的key不存在时，是会抛出KeyError异常的，也就是发生错误。（当然可以用 get 方法来避免这样的错误）\n",
    "\n",
    "如果使用defaultdict，只要你传入一个默认的方法，那么请求一个不存在的key时， 便会调用这个方法，使用其结果来作为这个key的默认值。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "David\n"
     ]
    }
   ],
   "source": [
    "# 标准的字典用法\n",
    "\n",
    "i = {'name':'David'}\n",
    "print(i['name'])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "ename": "KeyError",
     "evalue": "'score'",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mKeyError\u001b[0m                                  Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-23-f3bc1b97dc22>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m()\u001b[0m\n\u001b[0;32m      2\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      3\u001b[0m \u001b[0mi\u001b[0m \u001b[1;33m=\u001b[0m \u001b[1;33m{\u001b[0m\u001b[1;34m'name'\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;34m'David'\u001b[0m\u001b[1;33m}\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 4\u001b[1;33m \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mi\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;34m'score'\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;31mKeyError\u001b[0m: 'score'"
     ]
    }
   ],
   "source": [
    "# 不存在 key，则会报错\n",
    "\n",
    "i = {'name':'David'}\n",
    "print(i['score'])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "David\n"
     ]
    }
   ],
   "source": [
    "# defaultdict 举例\n",
    "\n",
    "from collections import defaultdict\n",
    "\n",
    "i = defaultdict(lambda: 100)\n",
    "i['name']='David'\n",
    "print(i['name'])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "100\n",
      "100\n"
     ]
    }
   ],
   "source": [
    "# default 返回默认值，不会报错\n",
    "\n",
    "print(i['score'])\n",
    "print(i['best_score'])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "David\n",
      "100\n"
     ]
    }
   ],
   "source": [
    "from collections import defaultdict\n",
    "\n",
    "i = defaultdict(lambda: '100')\n",
    "i['name']='David'\n",
    "print(i['name'])\n",
    "print(i['score'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "##### OrderedDict\n",
    "\n",
    "使用 dict 字典时，Key 是无序的。在对 dict 做迭代访问时，我们无法确定 Key 的顺序。\n",
    "\n",
    "如果要保持 Key 的顺序，可以用 OrderedDict，这是一个 Key 值有序的字典数据类型。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'c': 3, 'b': 2, 'a': 1}\n"
     ]
    }
   ],
   "source": [
    "# dict 是无序的\n",
    "\n",
    "d = dict([('a', 1), ('b', 2), ('c', 3)])\n",
    "\n",
    "print(d)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'c': 3, 'a': 1, 'b': 2}\n",
      "{'c': 3, 'a': 1, 'd': 4, 'b': 2}\n"
     ]
    }
   ],
   "source": [
    "# 传统dict 追加一对 key value\n",
    "\n",
    "d = dict([('a', 1), ('b', 2), ('c', 3)])\n",
    "\n",
    "print(d)\n",
    "\n",
    "d['d'] = 4\n",
    "print(d)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "OrderedDict([('a', 1), ('b', 2), ('c', 3)])\n"
     ]
    }
   ],
   "source": [
    "# 使用 OrderedDict\n",
    "\n",
    "from collections import OrderedDict\n",
    "\n",
    "d = OrderedDict()\n",
    "d['a'] = 1\n",
    "d['b'] = 2\n",
    "d['c'] = 3\n",
    "\n",
    "print(d)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "OrderedDict([('a', 1), ('b', 2), ('c', 3)])\n",
      "OrderedDict([('a', 1), ('b', 2), ('c', 3), ('d', 4)])\n"
     ]
    }
   ],
   "source": [
    "# 使用 OrderedDict, 追加一对 key value\n",
    "# OrderedDict的Key会按照插入的顺序排列，不是Key本身排序\n",
    "\n",
    "from collections import OrderedDict\n",
    "\n",
    "d = OrderedDict()\n",
    "d['a'] = 1\n",
    "d['b'] = 2\n",
    "d['c'] = 3\n",
    "\n",
    "print(d)\n",
    "\n",
    "d['d'] = 4\n",
    "print(d)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "add: ('a', 1)\n",
      "add: ('b', 2)\n",
      "add: ('c', 3)\n",
      "LastUpdatedOrderedDict([('a', 1), ('b', 2), ('c', 3)])\n"
     ]
    }
   ],
   "source": [
    "# OrderedDict可以实现一个FIFO（先进先出）的dict，当容量超出限制时，先删除最早添加的Key：\n",
    "\n",
    "from collections import OrderedDict\n",
    "\n",
    "class LastUpdatedOrderedDict(OrderedDict):\n",
    "\n",
    "    def __init__(self, capacity):\n",
    "        super(LastUpdatedOrderedDict, self).__init__()\n",
    "        self._capacity = capacity\n",
    "\n",
    "    def __setitem__(self, key, value):\n",
    "        containsKey = 1 if key in self else 0\n",
    "        if len(self) - containsKey >= self._capacity:\n",
    "            last = self.popitem(last=False)\n",
    "            print('remove:', last)\n",
    "        if containsKey:\n",
    "            del self[key]\n",
    "            print('set:', (key, value))\n",
    "        else:\n",
    "            print('add:', (key, value))\n",
    "        OrderedDict.__setitem__(self, key, value)\n",
    "        \n",
    "d = LastUpdatedOrderedDict(4)\n",
    "d['a'] = 1\n",
    "d['b'] = 2\n",
    "d['c'] = 3\n",
    "print(d)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "add: ('d', 4)\n",
      "remove: ('a', 1)\n",
      "add: ('e', 5)\n",
      "remove: ('b', 2)\n",
      "add: ('f', 6)\n",
      "LastUpdatedOrderedDict([('c', 3), ('d', 4), ('e', 5), ('f', 6)])\n"
     ]
    }
   ],
   "source": [
    "d['d'] = 4\n",
    "d['e'] = 5\n",
    "d['f'] = 6\n",
    "print(d)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "OrderedDict([('a', 1), ('b', 2), ('c', 3)])\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "OrderedDict([('b', 2), ('c', 3), ('new_key', 4)])"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 简化的先进先出Dict\n",
    "from collections import OrderedDict\n",
    "d = OrderedDict()\n",
    "d['a'] = 1\n",
    "d['b'] = 2\n",
    "d['c'] = 3\n",
    "print(d)\n",
    "\n",
    "# 3个参数：原始有序字典，容量限制，待插入的key，待插入的value\n",
    "def update_ordereddict(ordered_dict, len_limit ,key, value):\n",
    "    if len(ordered_dict) >=0 and len(ordered_dict) < len_limit:\n",
    "        ordered_dict[key]=value\n",
    "        return ordered_dict\n",
    "    else:\n",
    "        ordered_dict.popitem(last=False)\n",
    "        ordered_dict[key]=value\n",
    "        return ordered_dict\n",
    "\n",
    "# 插入一个新key-value\n",
    "update_ordereddict(d, 3, 'new_key', 4)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "##### Counter\n",
    "\n",
    "Counter是一个简单的计数器，例如，统计字符出现的个数："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[(' ', 6), ('s', 4), ('a', 3), ('c', 3), ('t', 2)]\n"
     ]
    }
   ],
   "source": [
    "# Counter类的目的是用来跟踪值出现的次数,以字典的键值对形式存储，其中元素作为key，其计数作为value\n",
    "# 下面这个例子就是使用 Counter 模块统计一段句子里面所有字符出现次数\n",
    "\n",
    "from collections import Counter\n",
    "\n",
    "s = 'A Counter is a dict subclass. '.lower()\n",
    "\n",
    "c = Counter(s)\n",
    "# 获取出现频率最高的5个字符\n",
    "print(c.most_common(5))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Please input:aaaabbbccdefghi\n",
      "[('a', 4), ('b', 3), ('c', 2), ('i', 1), ('d', 1)]\n"
     ]
    }
   ],
   "source": [
    "# Counter 举例\n",
    "\n",
    "from collections import Counter\n",
    "\n",
    "s = input('Please input:')\n",
    "\n",
    "s = s.lower()\n",
    "\n",
    "c = Counter(s)\n",
    "# 获取出现频率最高的5个字符\n",
    "print(c.most_common(5))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "3\n",
      "5\n",
      "0\n"
     ]
    }
   ],
   "source": [
    "# 计数值的访问与缺失的键\n",
    "\n",
    "print(c['a'])\n",
    "print(c['b'])\n",
    "print(c['z'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "---\n",
    "\n",
    "### 错误处理\n",
    "\n",
    "异常即是一个事件，该事件会在程序执行过程中发生，影响了程序的正常执行。一般情况下，在Python无法正常处理程序时就会发生一个异常。异常是Python对象，表示一个错误。当Python脚本发生异常时我们需要捕获处理它，否则程序会终止执行。\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a:a\n"
     ]
    },
    {
     "ename": "ValueError",
     "evalue": "invalid literal for int() with base 10: 'a'",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mValueError\u001b[0m                                Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-6-2e5fa3a10b86>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m()\u001b[0m\n\u001b[0;32m      1\u001b[0m \u001b[1;31m# 看似健壮的程序，输入字母会报错\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      2\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 3\u001b[1;33m \u001b[0ma\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0minput\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'a:'\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m      4\u001b[0m \u001b[1;32mif\u001b[0m \u001b[0ma\u001b[0m \u001b[1;33m==\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      5\u001b[0m     \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m\"you cannot input  0\"\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;31mValueError\u001b[0m: invalid literal for int() with base 10: 'a'"
     ]
    }
   ],
   "source": [
    "# 看似健壮的程序，输入字母会报错\n",
    "\n",
    "a = int(input('a:'))\n",
    "if a ==0:\n",
    "    print(\"you cannot input  0\")\n",
    "else: \n",
    "    r = 10 / a\n",
    "    print('result:', r)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a:0\n",
      "except: division by zero\n"
     ]
    }
   ],
   "source": [
    "# 防止输入0作为除数\n",
    "\n",
    "try:\n",
    "    a = int(input('a:'))\n",
    "    r = 10 / a\n",
    "    print('result:', r)\n",
    "except ZeroDivisionError as e:\n",
    "    print('except:',e)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a:0\n"
     ]
    },
    {
     "ename": "ZeroDivisionError",
     "evalue": "division by zero",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mZeroDivisionError\u001b[0m                         Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-13-7c4d145c6be1>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m()\u001b[0m\n\u001b[0;32m      1\u001b[0m \u001b[0ma\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0minput\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'a:'\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 2\u001b[1;33m \u001b[0mr\u001b[0m \u001b[1;33m=\u001b[0m \u001b[1;36m10\u001b[0m \u001b[1;33m/\u001b[0m \u001b[0ma\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;31mZeroDivisionError\u001b[0m: division by zero"
     ]
    }
   ],
   "source": [
    "a = int(input('a:'))\n",
    "r = 10 / a"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a:0\n",
      "except: <class 'Exception'> division by zero\n"
     ]
    }
   ],
   "source": [
    "# 如何捕捉多个类型的错误？\n",
    "\n",
    "\n",
    "try:\n",
    "    a = int(input('a:'))\n",
    "    r = 10 / a\n",
    "    print('result:', r)\n",
    "except ZeroDivisionError as e:\n",
    "    print('except:',e)\n",
    "except ValueError as e:\n",
    "    print('except:',e)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a:0\n",
      "except: division by zero\n",
      "finish...\n"
     ]
    }
   ],
   "source": [
    "# try...except...finally\n",
    "\n",
    "a = int(input('a:'))\n",
    "\n",
    "try:\n",
    "    r = 10 / a\n",
    "    print('result:', r)\n",
    "except ZeroDivisionError as e:\n",
    "    print('except:', e)\n",
    "finally:\n",
    "    print('finish...')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 69,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "except: name 'c' is not defined\n"
     ]
    }
   ],
   "source": [
    "# 防止错误举例\n",
    "\n",
    "a = 100\n",
    "try:\n",
    "    b = a + c \n",
    "    print(b)\n",
    "except NameError as e:\n",
    "    print('except:',e)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "except: name 'c' is not defined\n",
      "100\n"
     ]
    }
   ],
   "source": [
    "# 防止错误举例，略完善的做法\n",
    "\n",
    "a = 100\n",
    "\n",
    "try:\n",
    "    b = a + c \n",
    "except NameError as e:\n",
    "    print('except:', e)\n",
    "    b = a + 0\n",
    "print(b)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a:a\n",
      "except: invalid literal for int() with base 10: 'a'\n",
      "covert ok\n",
      "result: 10.0\n",
      "finish...\n"
     ]
    }
   ],
   "source": [
    "# 对 int 进行判断\n",
    "\n",
    "a = input('a:')\n",
    "\n",
    "# 对输入值转换还有更完善的方法\n",
    "try:\n",
    "    a = int(a)\n",
    "except ValueError as e:\n",
    "    print('except:', e)   \n",
    "    a = 1\n",
    "finally:\n",
    "    print('covert ok')\n",
    "\n",
    "    \n",
    "try:\n",
    "    r = 10 / a\n",
    "    print('result:', r)\n",
    "except ZeroDivisionError as e:\n",
    "    print('except:', e)\n",
    "finally:\n",
    "    print('finish...')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 单元测试\n",
    "用Python搭建自动化测试框架，我们需要组织用例以及测试执行，推荐Python的标准库——unittest。     \n",
    "unittest是xUnit系列框架中的一员，如果你了解xUnit的其他成员，那你用unittest来应该是很轻松的，它们的工作方式都差不多。\n",
    "\n",
    "官方说明文档： https://docs.python.org/3/library/unittest.html     \n",
    "The unittest module is actually a testing framework that was originally inspired by JUnit. It currently supports test automation, the sharing of setup and shutdown code, aggregating tests into collections and the independence of tests from the reporting framework.\n",
    "\n",
    "Unittest框架支持以下4个概念:\n",
    "* Test Fixture –对一个测试用例环境的搭建和销毁。     A fixture is what is used to setup a test so it can be run and also tears down when the test is finished. For example, you might need to create a temporary database before the test can be run and destroy it once the test finishes.    \n",
    "* Test Case – 一个TestCase的实例就是一个测试用例，通过运行这个测试单元，可以对某一个问题进行验证。 The test case is your actual test. It will typically check (or assert) that a specific response comes from a specific set of inputs. The unittest frameworks provides a base class called TestCase that you can use to create new test cases.     \n",
    "* Test Suite – 多个测试用例集合在一起，就是TestSuite。    The test suite is a collection of test cases, test suites or both.     \n",
    "* Test Runner – 是来执行测试用例的。     A runner is what controls or orchestrates the running of the tests or suites. It will also provide the outcome to the user (i.e. did they pass or fail). A runner can use a graphical user interface or be a simple text interface.\n",
    "\n",
    "我们使用PYTHON的单元测试的主要方法是:\n",
    "* assert-基础断言，允许用户自己扩展断言；\n",
    "* assertEqual(a, b) -检查a和b是否相等；\n",
    "* assertNotEqual(a, b) -检查a和b是否不相等；\n",
    "* assertIn(a, b) -检查a是否在b中；\n",
    "* assertNotIn(a, b) -检查a是否不在b中；\n",
    "* assertFalse(a) -检查a的值是否是FALSE；\n",
    "* assertTrue(a) -检查a的值是否是TRUE；\n",
    "* assertIsInstance(a, b) -检查a的类型是否是“b”；\n",
    "* assertRaises(ERROR, A, ARGS)-检查当A使用参数ARGS被调用的时候是否会引发ERROR；\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# 一个简单例子， 将该脚本保存为 mymath.py\n",
    "# 建立一个自己的脚本，定义了 add substract multiply divide 四个数学功能，我们用单元测试来检查它们是否与预期的功能一致！\n",
    "\n",
    "def add(a, b):\n",
    "    return a + b\n",
    " \n",
    "def subtract(a, b):\n",
    "    return a - b\n",
    " \n",
    "def multiply(a, b):\n",
    "    return a * b\n",
    " \n",
    "def divide(numerator, denominator):\n",
    "    return float(numerator) / denominator"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# 将以下脚本保存为 test_mymath.py，并运行\n",
    "\n",
    "\"\"\"\n",
    "构建unittest基本使用方法\n",
    "1.import unittest 导入unittest库\n",
    "2.定义一个继承自unittest.TestCase的测试用例类\n",
    "3.定义setUp和tearDown，在每个测试用例前后做一些辅助工作\n",
    "4.定义测试用例，函数名字以test开头\n",
    "5.一个测试用例应该只测试一个方面，测试目的和测试内容应很明确。主要是调用assertEqual、assertRaises等断言方法判断程序执行结果和预期值是否相符。\n",
    "6.调用unittest.main()启动测试\n",
    "7.如果测试未通过，会输出相应的错误提示。如果测试全部通过则不显示任何东西，这时可以添加-v参数显示详细信息。\n",
    "\"\"\"\n",
    "\n",
    "import mymath\n",
    "import unittest\n",
    " \n",
    "class TestAdd(unittest.TestCase):\n",
    "    \"\"\"\n",
    "    Test the add function from the mymath library\n",
    "    \"\"\"\n",
    " \n",
    "    def test_add_integers(self):\n",
    "        \"\"\"\n",
    "        Test that the addition of two integers returns the correct total\n",
    "        \"\"\"\n",
    "        result = mymath.add(1, 2)\n",
    "        self.assertNotEqual(result, 555)\n",
    "        self.assertEqual(result, 3)\n",
    " \n",
    "    def test_add_floats(self):\n",
    "        \"\"\"\n",
    "        Test that the addition of two floats returns the correct result\n",
    "        \"\"\"\n",
    "        result = mymath.add(10.5, 2)\n",
    "        self.assertEqual(result, 12.5)\n",
    " \n",
    "    def test_add_strings(self):\n",
    "        \"\"\"\n",
    "        Test the addition of two strings returns the two string as one\n",
    "        concatenated string\n",
    "        \"\"\"\n",
    "        result = mymath.add('abc', 'def')\n",
    "        self.assertEqual(result, 'abcdef')\n",
    " \n",
    " \n",
    "if __name__ == '__main__':\n",
    "    unittest.main()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.5.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}
